#include <stdio.h> 
#include <stdlib.h>
#include <mpi.h>
#include <stdint.h>
#include <string.h>
int main(int argc, char *argv[])
{
	
    int nodeRank, numNodes, length;
	char name[256]; 
	MPI_Init(&argc,&argv);
	MPI_Comm_rank(MPI_COMM_WORLD,&nodeRank);
	MPI_Comm_size(MPI_COMM_WORLD,&numNodes);
	MPI_Get_processor_name(name, &length); 

	MPI_Status status; 

	//printf("nodeRank: %d\tnumNodes: %d\tProcName: %s\n", nodeRank, numNodes, name); 
	

	MPI_File fh; 

	int openErr = MPI_File_open(MPI_COMM_WORLD, argv[1], MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);


	char buffer;
	char widthStr[256];
	char heightStr[256];
	char maxValStr[256];
	char saveHeader[256]; 
	uint32_t headerOffset = 0; 
	uint32_t width;
	uint32_t height; 
	uint32_t maxVal;	

	// Read header information
	// Moves magic number out of the way
	MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
	saveHeader[headerOffset] = buffer; 
	headerOffset ++; 
	MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
	saveHeader[headerOffset] = buffer; 
	headerOffset ++; 

	
	// Gets width of image
	int i;
	buffer = ' '; 
	for(i=0; (buffer > 47) && (buffer < 58) || i==0; i++)
	{
		while(buffer < 47 || buffer > 58){
			MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
			saveHeader[headerOffset] = buffer; 
			headerOffset++;
		}	
		widthStr[i] = buffer;
		//if(nodeRank == 0)
			//printf("buffer: %c\n", buffer);  
		MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
		saveHeader[headerOffset] = buffer; 
		headerOffset++; 
	}
	widthStr[i] = '\0';
	//printf("widthStr: %s", widthStr); 
	width = atoi(widthStr); 
	
	// Gets height of image	
	for(i=0; (buffer > 47) && (buffer < 58) || i==0; i++)
	{
		while(buffer < 47 || buffer > 58){
			MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
			saveHeader[headerOffset] = buffer; 
			headerOffset++;
		}	
		heightStr[i] = buffer; 
		MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
		saveHeader[headerOffset] = buffer; 
		headerOffset++; 
	}
	heightStr[i] = '\0';
	height = atoi(heightStr); 
	

	// Gets maxVal of image	
	for(i=0; (buffer > 47) && (buffer < 58) || i==0; i++)
	{
		while(buffer < 47 || buffer > 58){
			MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
			saveHeader[headerOffset] = buffer; 
			headerOffset++;
		}	
		maxValStr[i] = buffer; 
		MPI_File_read(fh, &buffer, 1, MPI_CHAR, &status);
		saveHeader[headerOffset] = buffer; 
		headerOffset++; 
	}
	maxValStr[i] = '\0';
	maxVal = atoi(maxValStr); 

	// Create structures to split up reads/writes among nodes
	int nodeSqrt = numNodes;
	int count = 0;
	while(nodeSqrt > 1)
	{
		nodeSqrt /= 2;
		count++;
	}
	int sideCount = 1;
	for(i=0; i<count/2; i++){
		sideCount *= 2;
	}


	MPI_Datatype RGB;	// Holds values for red/green/blue
	MPI_Type_contiguous(3, MPI_BYTE, &RGB); 
	MPI_Type_commit(&RGB);
	int frameS[2];
	frameS[1] = width;
	frameS[0] = height; 
	int subS[2];
	subS[1] = width/sideCount; 
	subS[0] = height/sideCount; 
	int startPos[2];
	startPos[1] = subS[0]*(nodeRank%sideCount);
	startPos[0] = subS[1]*(nodeRank/sideCount);

	int cords[2];
	cords[1] = nodeRank%sideCount;
	cords[0] = nodeRank/sideCount; 

	/*
	if(nodeRank==0){
		printf("HeaderOffset: %d\twidth: %d\theight: %d\tsideCount: %d\n", headerOffset, width, height, sideCount); 
		printf("sub1: %d\t\tsub2: %d\n", subS[0], subS[1]); 
	}
	*/


	// Setup up file view
	MPI_Datatype subArr;
	MPI_Type_create_subarray(2, frameS, subS, startPos, MPI_ORDER_C, RGB, &subArr); 
	MPI_Type_commit(&subArr);

	MPI_File_set_view(fh, headerOffset, RGB, subArr, "native", MPI_INFO_NULL);

	MPI_Aint size; 
	MPI_Type_extent(RGB, &size);  

	// Collect image data
	void * rgbBuffer = malloc(size*subS[0]*subS[1]); 
	MPI_File_read_all(fh, rgbBuffer, subS[0]*subS[1], RGB, &status);

	// Flip the square
	if((cords[0]%2 == 1 && cords[1]%2 == 0) || (cords[1]%2 == 1 && cords[0]%2 == 0))
	{
		void * tempPix = malloc(size);
		for(int i=0; i<subS[0]; i++)
			for(int j=0; j<subS[1]/2; j++)
			{
				memcpy(tempPix, &rgbBuffer[size*(i*subS[0]+j)], size);
				memcpy(&rgbBuffer[size*(i*subS[0]+j)], &rgbBuffer[size*(i*subS[0]+subS[1]-j-1)], size);
				memcpy(&rgbBuffer[size*(i*subS[0]+subS[1]-j-1)], tempPix, size);
			}

				
	}

	// Write out
	MPI_File fhOut; 
	MPI_File_open(MPI_COMM_WORLD, argv[2], MPI_MODE_CREATE|MPI_MODE_WRONLY, MPI_INFO_NULL, &fhOut); 
	
	MPI_File_write(fhOut, saveHeader, headerOffset, MPI_BYTE, &status); 
	MPI_File_set_view(fhOut, headerOffset, RGB, subArr, "native", MPI_INFO_NULL); 
	MPI_File_write_all(fhOut, rgbBuffer, subS[0]*subS[1], RGB, &status);  

	MPI_File_close(&fh); 
	MPI_File_close(&fhOut); 

	MPI_Finalize(); 

	return 0; 
}
